/*      */ package com.zimbra.cs.mime;
/*      */ 
/*      */ import com.google.common.base.Strings;
/*      */ import com.google.common.collect.ImmutableSet;
/*      */ import com.ibm.icu.text.CharsetDetector;
/*      */ import com.ibm.icu.text.CharsetMatch;
/*      */ import com.zimbra.common.localconfig.KnownKey;
/*      */ import com.zimbra.common.localconfig.LC;
/*      */ import com.zimbra.common.mime.ContentDisposition;
/*      */ import com.zimbra.common.mime.ContentType;
/*      */ import com.zimbra.common.mime.shim.JavaMailInternetAddress;
/*      */ import com.zimbra.common.util.ByteUtil;
/*      */ import com.zimbra.common.util.CharsetUtil;
/*      */ import com.zimbra.common.util.Log;
/*      */ import com.zimbra.common.util.LogFactory;
/*      */ import com.zimbra.common.util.StringUtil;
/*      */ import com.zimbra.common.util.ZimbraLog;
/*      */ import com.zimbra.common.zmime.ZInternetHeader;
/*      */ import com.zimbra.common.zmime.ZMimeBodyPart;
/*      */ import com.zimbra.common.zmime.ZMimeMessage;
/*      */ import com.zimbra.common.zmime.ZMimeMultipart;
/*      */ import com.zimbra.common.zmime.ZMimePart;
/*      */ import com.zimbra.cs.account.Account;
/*      */ import com.zimbra.cs.util.JMSession;
/*      */ import com.zimbra.cs.util.Zimbra;
/*      */ import java.io.BufferedInputStream;
/*      */ import java.io.ByteArrayInputStream;
/*      */ import java.io.ByteArrayOutputStream;
/*      */ import java.io.FilterInputStream;
/*      */ import java.io.IOException;
/*      */ import java.io.InputStream;
/*      */ import java.io.InputStreamReader;
/*      */ import java.io.OutputStream;
/*      */ import java.io.PipedInputStream;
/*      */ import java.io.PipedOutputStream;
/*      */ import java.io.PrintStream;
/*      */ import java.io.Reader;
/*      */ import java.io.UnsupportedEncodingException;
/*      */ import java.net.URLDecoder;
/*      */ import java.nio.charset.Charset;
/*      */ import java.util.ArrayList;
/*      */ import java.util.Arrays;
/*      */ import java.util.Collections;
/*      */ import java.util.HashSet;
/*      */ import java.util.LinkedHashSet;
/*      */ import java.util.LinkedList;
/*      */ import java.util.List;
/*      */ import java.util.Set;
/*      */ import java.util.UUID;
/*      */ import javax.activation.DataHandler;
/*      */ import javax.activation.DataSource;
/*      */ import javax.mail.Address;
/*      */ import javax.mail.BodyPart;
/*      */ import javax.mail.Message.RecipientType;
/*      */ import javax.mail.MessagingException;
/*      */ import javax.mail.Multipart;
/*      */ import javax.mail.Session;
/*      */ import javax.mail.internet.AddressException;
/*      */ import javax.mail.internet.InternetHeaders;
/*      */ import javax.mail.internet.MimeBodyPart;
/*      */ import javax.mail.internet.MimeMessage;
/*      */ import javax.mail.internet.MimeMultipart;
/*      */ import javax.mail.internet.MimePart;
/*      */ import javax.mail.internet.MimeUtility;
/*      */ import javax.mail.internet.ParseException;
/*      */ import javax.mail.util.ByteArrayDataSource;
/*      */ import javax.mail.util.SharedFileInputStream;
/*      */ import org.apache.commons.codec.EncoderException;
/*      */ import org.apache.commons.codec.net.QCodec;
/*      */ import org.apache.commons.io.IOUtils;
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ public class Mime
/*      */ {
/*   99 */   private static Log sLog = LogFactory.getLog(Mime.class);
/*      */   
/*      */   private static final int MAX_DECODE_BUFFER = 2048;
/*      */   
/*  103 */   private static final Set<String> TRANSFER_ENCODINGS = ImmutableSet.of("7bit", "8bit", "binary", "quoted-printable", "base64");
/*      */   
/*      */ 
/*  106 */   private static final Set<String> INLINEABLE_TYPES = ImmutableSet.of("image/jpeg", "image/png", "image/gif");
/*      */   
/*  108 */   private static Set<String> TEXT_ALTERNATES = ImmutableSet.of("text/enriched", "text/html");
/*      */   
/*  110 */   private static Set<String> HTML_ALTERNATES = ImmutableSet.of("text/enriched", "text/plain");
/*      */   
/*  112 */   private static Set<String> KNOWN_MULTIPART_TYPES = ImmutableSet.of("multipart/alternative", "multipart/digest", "multipart/mixed", "multipart/report", "multipart/related", "multipart/signed", new String[] { "multipart/encrypted" });
/*      */   
/*      */ 
/*      */   private static final int MAX_PREAMBLE_LENGTH = 1024;
/*      */   
/*      */ 
/*      */ 
/*      */   public static class FixedMimeMessage
/*      */     extends ZMimeMessage
/*      */   {
/*      */     public FixedMimeMessage(Session session)
/*      */     {
/*  124 */       super();
/*      */     }
/*      */     
/*      */     public FixedMimeMessage(Session session, InputStream is) throws MessagingException {
/*  128 */       super(is);
/*      */     }
/*      */     
/*      */     public FixedMimeMessage(MimeMessage source) throws MessagingException {
/*  132 */       super();
/*      */     }
/*      */     
/*      */     public FixedMimeMessage(MimeMessage source, Account acct) throws MessagingException {
/*  136 */       super();
/*  137 */       if (acct != null) {
/*  138 */         setProperty("mail.mime.charset", acct.getPrefMailDefaultCharset());
/*      */       }
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */     protected void updateMessageID()
/*      */       throws MessagingException
/*      */     {
/*  147 */       if (getMessageID() == null) {
/*  148 */         super.updateMessageID();
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static List<MPartInfo> getParts(MimeMessage mm)
/*      */     throws IOException, MessagingException
/*      */   {
/*  161 */     return getParts(mm, null);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static List<MPartInfo> getParts(MimeMessage mm, String defaultCharset)
/*      */     throws IOException, MessagingException
/*      */   {
/*  173 */     List<MPartInfo> parts = listParts(mm, defaultCharset);
/*  174 */     Set<MPartInfo> bodies = getBody(parts, true);
/*  175 */     for (MPartInfo mpi : parts) {
/*  176 */       mpi.mIsFilterableAttachment = isFilterableAttachment(mpi, bodies);
/*  177 */       if (mpi.mIsFilterableAttachment)
/*  178 */         mpi.mIsToplevelAttachment = ((bodies == null) || (!bodies.contains(mpi)) || (!INLINEABLE_TYPES.contains(mpi.mContentType)));
/*      */     }
/*  180 */     return parts;
/*      */   }
/*      */   
/*      */   private static List<MPartInfo> listParts(MimePart root, String defaultCharset) throws MessagingException, IOException {
/*  184 */     List<MPartInfo> parts = new ArrayList();
/*      */     
/*  186 */     LinkedList<MPartInfo> queue = new LinkedList();
/*  187 */     queue.add(generateMPartInfo(root, null, "", 0));
/*      */     
/*  189 */     MimeMultipart emptyMultipart = null;
/*  190 */     while (!queue.isEmpty()) {
/*  191 */       MPartInfo mpart = (MPartInfo)queue.removeFirst();
/*  192 */       MimePart mp = mpart.getMimePart();
/*  193 */       parts.add(mpart);
/*      */       
/*  195 */       String cts = mpart.mContentType;
/*  196 */       boolean isMultipart = cts.startsWith("multipart/");
/*  197 */       boolean isMessage = (!isMultipart) && (cts.equals("message/rfc822"));
/*      */       
/*  199 */       if (isMultipart)
/*      */       {
/*  201 */         String prefix = mpart.mPartName.length() > 0 ? mpart.mPartName + '.' : "";
/*  202 */         if ((mp instanceof MimeMessage)) {
/*  203 */           mpart.mPartName = (prefix + "TEXT");
/*      */         }
/*  205 */         MimeMultipart multi = getMultipartContent(mp, cts);
/*  206 */         if (multi != null) {
/*  207 */           if ((multi.getCount() == 0) && (LC.mime_promote_empty_multipart.booleanValue())) {
/*  208 */             if (emptyMultipart == null) {
/*  209 */               emptyMultipart = multi;
/*      */             }
/*  211 */             if ("multipart/appledouble".equalsIgnoreCase(getContentType(mp))) {
/*  212 */               ZimbraLog.misc.debug("appledouble with no children; assuming it is malformed and really applefile");
/*  213 */               mpart.mContentType = mpart.mContentType.replace("multipart/appledouble", "application/applefile");
/*      */             }
/*      */           }
/*  216 */           mpart.mChildren = new ArrayList(multi.getCount());
/*  217 */           for (int i = 1; i <= multi.getCount(); i++) {
/*  218 */             mpart.mChildren.add(generateMPartInfo((MimePart)multi.getBodyPart(i - 1), mpart, prefix + i, i));
/*      */           }
/*  220 */           queue.addAll(0, mpart.mChildren);
/*      */         }
/*  222 */       } else if (isMessage) {
/*  223 */         MimeMessage mm = getMessageContent(mp);
/*  224 */         if (mm != null) {
/*  225 */           MPartInfo child = generateMPartInfo(mm, mpart, mpart.mPartName, 0);
/*  226 */           queue.addFirst(child);
/*  227 */           mpart.mChildren = Arrays.asList(new MPartInfo[] { child });
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*  234 */     if ((emptyMultipart != null) && (parts.size() == 1)) {
/*  235 */       String text = emptyMultipart.getPreamble();
/*  236 */       if (!StringUtil.isNullOrEmpty(text)) {
/*  237 */         ZimbraLog.misc.debug("single multipart with no children. promoting the preamble into a single text part");
/*  238 */         parts.remove(0);
/*  239 */         MPartInfo mpart = new MPartInfo();
/*  240 */         ZMimeBodyPart mp = new ZMimeBodyPart();
/*  241 */         mp.setText(text, defaultCharset);
/*  242 */         mpart.mPart = mp;
/*  243 */         mpart.mContentType = mp.getContentType();
/*  244 */         mpart.mDisposition = "";
/*  245 */         mpart.mPartName = "1";
/*  246 */         parts.add(mpart);
/*      */       }
/*      */     }
/*      */     
/*  250 */     return parts;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static void fixBase64MimePartLineFolding(MimeMessage mm)
/*      */     throws MessagingException, IOException
/*      */   {
/*  264 */     List<MPartInfo> mList = getParts(mm);
/*  265 */     for (MPartInfo mPartInfo : mList) {
/*  266 */       String ct = mPartInfo.getMimePart().getHeader("Content-Transfer-Encoding", ":");
/*  267 */       if ("base64".equalsIgnoreCase(ct)) {
/*  268 */         InputStream io = mPartInfo.getMimePart().getInputStream();
/*  269 */         String ctype = mPartInfo.getMimePart().getContentType();
/*  270 */         ByteArrayOutputStream bos = new ByteArrayOutputStream();
/*  271 */         IOUtils.copy(io, bos);
/*  272 */         DataSource ds = new ByteArrayDataSource(bos.toByteArray(), ctype);
/*  273 */         DataHandler dh = new DataHandler(ds);
/*  274 */         mPartInfo.getMimePart().setDataHandler(dh);
/*  275 */         mPartInfo.getMimePart().setHeader("Content-Transfer-Encoding", ct);
/*  276 */         mPartInfo.getMimePart().setHeader("Content-Type", ctype);
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */   private static MPartInfo generateMPartInfo(MimePart mp, MPartInfo parent, String prefix, int partNum) {
/*  282 */     boolean inDigest = (parent != null) && (parent.mContentType.equals("multipart/digest"));
/*  283 */     String ctdefault = inDigest ? "message/rfc822" : "text/plain";
/*  284 */     String cts = getContentType(mp, ctdefault);
/*      */     
/*  286 */     String disp = null;String filename = getFilename(mp);
/*  287 */     int size = 0;
/*      */     try {
/*  289 */       disp = mp.getDisposition();
/*      */     }
/*      */     catch (Exception e) {}
/*      */     try {
/*  293 */       size = mp.getSize();
/*      */     }
/*      */     catch (MessagingException me) {}
/*      */     
/*      */ 
/*  298 */     boolean isMultipart = cts.startsWith("multipart/");
/*  299 */     if ((!isMultipart) && ((mp instanceof MimeMessage))) {
/*  300 */       prefix = (prefix.length() > 0 ? prefix + "." : "") + '1';
/*      */     }
/*  302 */     MPartInfo mpart = new MPartInfo();
/*  303 */     mpart.mPart = mp;
/*  304 */     mpart.mParent = parent;
/*  305 */     mpart.mContentType = cts;
/*  306 */     mpart.mPartName = prefix;
/*  307 */     mpart.mPartNum = partNum;
/*  308 */     mpart.mSize = size;
/*  309 */     mpart.mChildren = null;
/*  310 */     mpart.mDisposition = (disp == null ? "" : (inDigest) && (cts.equals("message/rfc822")) ? "attachment" : disp.toLowerCase());
/*  311 */     mpart.mFilename = (filename == null ? "" : filename);
/*  312 */     return mpart;
/*      */   }
/*      */   
/*      */   private static boolean isZimbraJavaMailShim(Object o) {
/*  316 */     return (ZMimeMessage.usingZimbraParser()) && (((o instanceof ZMimePart)) || ((o instanceof ZMimeMultipart)));
/*      */   }
/*      */   
/*      */   private static MimeMultipart validateMultipart(MimeMultipart multi, MimePart mp) throws MessagingException, IOException
/*      */   {
/*  321 */     if ((multi == null) || (isZimbraJavaMailShim(multi))) {
/*  322 */       return multi;
/*      */     }
/*      */     
/*  325 */     ContentType ctype = new ContentType(mp.getContentType());
/*      */     try {
/*  327 */       if ((!ctype.containsParameter("generated")) && (!findStartBoundary(mp, ctype.getParameter("boundary")))) {
/*  328 */         return new ZMimeMultipart(new RawContentMultipartDataSource(mp, ctype));
/*      */       }
/*  330 */       multi.getCount();
/*      */     } catch (ParseException pe) {
/*  332 */       multi = new ZMimeMultipart(new FixedMultipartDataSource(mp, ctype));
/*      */     } catch (MessagingException me) {
/*  334 */       multi = new ZMimeMultipart(new FixedMultipartDataSource(mp, ctype));
/*      */     }
/*  336 */     return multi;
/*      */   }
/*      */   
/*      */   private static boolean findStartBoundary(MimePart mp, String boundary)
/*      */     throws IOException
/*      */   {
/*  342 */     InputStream is = null;
/*      */     try {
/*  344 */       is = getRawInputStream(mp);
/*      */     } catch (MessagingException me) {
/*  346 */       return true;
/*      */     }
/*      */     
/*  349 */     int blength = boundary == null ? 0 : boundary.length();
/*  350 */     int bindex = 0;int dashes = 0;
/*  351 */     boolean failed = false;
/*      */     try {
/*  353 */       for (int i = 0; i < 1024; i++) {
/*  354 */         int c = is.read();
/*  355 */         boolean bool1; if (c == -1)
/*  356 */           return false;
/*  357 */         if ((c == 13) || (c == 10)) {
/*  358 */           if ((!failed) && (boundary == null ? bindex > 0 : bindex == blength))
/*  359 */             return true;
/*  360 */           bindex = dashes = 0;
/*  361 */           failed = false;
/*  362 */         } else if (!failed)
/*      */         {
/*  364 */           if (dashes != 2) {
/*  365 */             if (c == 45) {
/*  366 */               dashes++;
/*      */             } else
/*  368 */               failed = true;
/*  369 */           } else if (boundary == null) {
/*  370 */             if (Character.isWhitespace(c))
/*  371 */               failed = true;
/*  372 */             bindex++;
/*      */           }
/*  374 */           else if ((bindex >= blength) || (c != boundary.charAt(bindex++))) {
/*  375 */             failed = true;
/*      */           }
/*      */         }
/*      */       }
/*  379 */     } finally { ByteUtil.closeStream(is);
/*      */     }
/*  381 */     return false;
/*      */   }
/*      */   
/*      */   static InputStream getRawInputStream(MimePart mp) throws MessagingException {
/*  385 */     if ((mp instanceof MimeBodyPart))
/*  386 */       return ((MimeBodyPart)mp).getRawInputStream();
/*  387 */     if ((mp instanceof MimeMessage))
/*  388 */       return ((MimeMessage)mp).getRawInputStream();
/*  389 */     return new ByteArrayInputStream(new byte[0]);
/*      */   }
/*      */   
/*      */   private static class FixedMultipartDataSource implements DataSource {
/*      */     private final MimePart mMimePart;
/*      */     private final ContentType mContentType;
/*      */     
/*      */     FixedMultipartDataSource(MimePart mp, ContentType ctype) {
/*  397 */       this.mMimePart = mp;
/*  398 */       this.mContentType = new ContentType(ctype).cleanup();
/*      */     }
/*      */     
/*      */     public ContentType getParsedContentType() {
/*  402 */       return this.mContentType;
/*      */     }
/*      */     
/*      */     public String getContentType()
/*      */     {
/*  407 */       return this.mContentType.toString();
/*      */     }
/*      */     
/*      */     public String getName()
/*      */     {
/*  412 */       return null;
/*      */     }
/*      */     
/*      */     public OutputStream getOutputStream()
/*      */     {
/*  417 */       throw new UnsupportedOperationException();
/*      */     }
/*      */     
/*      */     public InputStream getInputStream() throws IOException
/*      */     {
/*      */       try {
/*  423 */         return Mime.getRawInputStream(this.mMimePart);
/*      */       } catch (MessagingException e) {
/*  425 */         IOException ioex = new IOException("failed to get raw input stream for mime part");
/*  426 */         ioex.initCause(e);
/*  427 */         throw ioex;
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */   private static class RawContentMultipartDataSource extends Mime.FixedMultipartDataSource {
/*      */     RawContentMultipartDataSource(MimePart mp, ContentType ctype) {
/*  434 */       super(ctype);
/*      */     }
/*      */     
/*      */     public InputStream getInputStream() throws IOException
/*      */     {
/*  439 */       return new RawContentInputStream(super.getInputStream());
/*      */     }
/*      */     
/*      */ 
/*      */     private class RawContentInputStream
/*      */       extends FilterInputStream
/*      */     {
/*  446 */       private int mPrologueIndex = 0; private int mEpilogueIndex = 0;
/*  447 */       private boolean mInPrologue = true; private boolean mInContent = false; private boolean mInEpilogue = false;
/*      */       private final String mBoundary;
/*      */       
/*  450 */       RawContentInputStream(InputStream is) { super();
/*      */         
/*  452 */         String explicitBoundary = Mime.RawContentMultipartDataSource.this.getParsedContentType().getParameter("boundary");
/*  453 */         this.mBoundary = (explicitBoundary == null ? "_-_" + UUID.randomUUID().toString() : explicitBoundary);
/*  454 */         byte[] boundary = this.mBoundary.getBytes();
/*      */         
/*  456 */         this.mPrologue = new byte[2 + boundary.length + 4];
/*  457 */         this.mPrologue[0] = (this.mPrologue[1] = 45);
/*  458 */         System.arraycopy(boundary, 0, this.mPrologue, 2, boundary.length);
/*  459 */         this.mPrologue[(boundary.length + 2)] = (this.mPrologue[(boundary.length + 4)] = 13);
/*  460 */         this.mPrologue[(boundary.length + 3)] = (this.mPrologue[(boundary.length + 5)] = 10);
/*      */         
/*  462 */         this.mEpilogue = new byte[4 + boundary.length + 4];
/*  463 */         this.mEpilogue[0] = (this.mEpilogue[(boundary.length + 6)] = 13);
/*  464 */         this.mEpilogue[1] = (this.mEpilogue[(boundary.length + 7)] = 10);
/*  465 */         this.mEpilogue[2] = (this.mEpilogue[3] = 45);
/*  466 */         System.arraycopy(boundary, 0, this.mEpilogue, 4, boundary.length);
/*  467 */         this.mEpilogue[(boundary.length + 4)] = (this.mEpilogue[(boundary.length + 5)] = 45);
/*      */       }
/*      */       
/*      */       public int available() throws IOException
/*      */       {
/*  472 */         return this.mPrologue.length - this.mPrologueIndex + super.available() + this.mEpilogue.length - this.mEpilogueIndex;
/*      */       }
/*      */       
/*      */       public int read() throws IOException
/*      */       {
/*      */         int c;
/*  478 */         if (this.mInPrologue) {
/*  479 */           int c = this.mPrologue[(this.mPrologueIndex++)];
/*  480 */           if (this.mPrologueIndex >= this.mPrologue.length) {
/*  481 */             this.mInPrologue = false;
/*  482 */             this.mInContent = true;
/*      */           }
/*  484 */         } else if (this.mInContent) {
/*  485 */           int c = super.read();
/*  486 */           if (c == -1) {
/*  487 */             c = this.mEpilogue[0];
/*  488 */             this.mEpilogueIndex = 1;
/*  489 */             this.mInContent = false;
/*  490 */             this.mInEpilogue = true;
/*      */           }
/*  492 */         } else if (this.mInEpilogue) {
/*  493 */           int c = this.mEpilogue[(this.mEpilogueIndex++)];
/*  494 */           if (this.mEpilogueIndex >= this.mEpilogue.length) {
/*  495 */             this.mInEpilogue = false;
/*      */           }
/*      */         } else {
/*  498 */           c = -1;
/*      */         }
/*  500 */         return c;
/*      */       }
/*      */       
/*      */       public int read(byte[] b, int off, int len) throws IOException
/*      */       {
/*  505 */         if (b == null)
/*  506 */           throw new NullPointerException();
/*  507 */         if ((off < 0) || (off > b.length) || (len < 0) || (off + len > b.length) || (off + len < 0))
/*  508 */           throw new IndexOutOfBoundsException();
/*  509 */         if (len == 0)
/*  510 */           return 0;
/*  511 */         if ((!this.mInPrologue) && (!this.mInContent) && (!this.mInEpilogue)) {
/*  512 */           return -1;
/*      */         }
/*      */         
/*  515 */         int remaining = len;
/*  516 */         if (this.mInPrologue) {
/*  517 */           int prologue = Math.min(remaining, this.mPrologue.length - this.mPrologueIndex);
/*  518 */           System.arraycopy(this.mPrologue, this.mPrologueIndex, b, off, prologue);
/*  519 */           this.mPrologueIndex += prologue;
/*  520 */           if (this.mPrologueIndex >= this.mPrologue.length) {
/*  521 */             this.mInPrologue = false;
/*  522 */             this.mInContent = true;
/*      */           }
/*  524 */           remaining -= prologue;
/*  525 */           off += prologue;
/*      */         }
/*  527 */         if (remaining == 0) {
/*  528 */           return len;
/*      */         }
/*      */         
/*  531 */         if (this.mInContent) {
/*  532 */           int content = super.read(b, off, remaining);
/*  533 */           if (content == -1) {
/*  534 */             this.mInContent = false;
/*  535 */             this.mInEpilogue = true;
/*      */           } else {
/*  537 */             remaining -= content;
/*  538 */             off += content;
/*      */           }
/*      */         }
/*  541 */         if (remaining == 0) {
/*  542 */           return len;
/*      */         }
/*      */         
/*  545 */         if (this.mInEpilogue) {
/*  546 */           int epilogue = Math.min(remaining, this.mEpilogue.length - this.mEpilogueIndex);
/*  547 */           System.arraycopy(this.mEpilogue, this.mEpilogueIndex, b, off, epilogue);
/*  548 */           this.mEpilogueIndex += epilogue;
/*  549 */           if (this.mEpilogueIndex >= this.mEpilogue.length) {
/*  550 */             this.mInEpilogue = false;
/*      */           }
/*  552 */           remaining -= epilogue;
/*  553 */           off += epilogue;
/*      */         }
/*  555 */         return len - remaining;
/*      */       }
/*      */       
/*      */       private final byte[] mPrologue;
/*      */       private final byte[] mEpilogue;
/*      */     }
/*      */   }
/*      */   
/*      */   public static MimeMessage getMessageContent(MimePart message822Part) throws IOException, MessagingException
/*      */   {
/*  565 */     String ctype = getContentType(message822Part);
/*  566 */     if ("message/rfc822".equals(ctype))
/*      */     {
/*  568 */       Object content = message822Part.getContent();
/*  569 */       if ((content instanceof MimeMessage)) {
/*  570 */         return (MimeMessage)content;
/*      */       }
/*      */     }
/*  573 */     InputStream is = null;
/*      */     try
/*      */     {
/*  576 */       return new FixedMimeMessage(JMSession.getSession(), is = message822Part.getInputStream());
/*      */     }
/*      */     catch (Exception e) {}finally {
/*  579 */       ByteUtil.closeStream(is);
/*      */     }
/*  581 */     return null;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   public static MimeMultipart getMultipartContent(MimePart multipartPart, String contentType)
/*      */     throws IOException, MessagingException
/*      */   {
/*  589 */     MimeMultipart mmp = null;
/*  590 */     Object content = multipartPart.getContent();
/*  591 */     if ((content instanceof MimeMultipart)) {
/*  592 */       mmp = (MimeMultipart)content;
/*  593 */     } else if ((content instanceof InputStream)) {
/*      */       try
/*      */       {
/*  596 */         mmp = new ZMimeMultipart(new InputStreamDataSource((InputStream)content, contentType));
/*      */       }
/*      */       catch (Exception e) {}finally {
/*  599 */         ByteUtil.closeStream((InputStream)content);
/*      */       }
/*      */     }
/*      */     
/*  603 */     return validateMultipart(mmp, multipartPart);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static String getStringContent(MimePart textPart, String defaultCharset)
/*      */     throws IOException, MessagingException
/*      */   {
/*  613 */     repairTransferEncoding(textPart);
/*  614 */     return decodeText(textPart.getInputStream(), textPart.getContentType(), defaultCharset);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   @Deprecated
/*      */   public static Reader getContentAsReader(MimePart textPart, String defaultCharset)
/*      */     throws IOException, MessagingException
/*      */   {
/*  631 */     repairTransferEncoding(textPart);
/*  632 */     return getTextReader(textPart.getInputStream(), textPart.getContentType(), defaultCharset);
/*      */   }
/*      */   
/*      */   public static void recursiveRepairTransferEncoding(MimeMessage mm) throws MessagingException, IOException {
/*  636 */     for (MPartInfo mpi : listParts(mm, null)) {
/*  637 */       repairTransferEncoding(mpi.mPart);
/*      */     }
/*      */   }
/*      */   
/*      */   public static void repairTransferEncoding(MimePart mp) throws MessagingException {
/*  642 */     if (isZimbraJavaMailShim(mp)) {
/*  643 */       return;
/*      */     }
/*      */     
/*  646 */     String cte = mp.getHeader("Content-Transfer-Encoding", null);
/*  647 */     String ct = getContentType(mp);
/*  648 */     if ((cte != null) && ((!TRANSFER_ENCODINGS.contains(cte.toLowerCase().trim())) || (ct.startsWith("multipart/")) || (ct.equals("message/rfc822"))))
/*      */     {
/*  650 */       mp.removeHeader("Content-Transfer-Encoding"); }
/*      */   }
/*      */   
/*      */   private static final class InputStreamDataSource implements DataSource {
/*      */     private final InputStream is;
/*      */     private final String type;
/*      */     
/*      */     InputStreamDataSource(InputStream stream, String contentType) {
/*  658 */       this.is = stream;
/*  659 */       this.type = contentType;
/*      */     }
/*      */     
/*      */     public String getContentType()
/*      */     {
/*  664 */       return this.type;
/*      */     }
/*      */     
/*      */     public String getName()
/*      */     {
/*  669 */       return null;
/*      */     }
/*      */     
/*      */     public InputStream getInputStream()
/*      */     {
/*  674 */       return this.is;
/*      */     }
/*      */     
/*      */     public OutputStream getOutputStream()
/*      */     {
/*  679 */       return null;
/*      */     }
/*      */   }
/*      */   
/*      */   private static boolean isEmlAttachment(MimePart mp) {
/*  684 */     String filename = getFilename(mp);
/*  685 */     return (filename != null) && ((filename.endsWith(".eml")) || (filename.endsWith(".msg")));
/*      */   }
/*      */   
/*      */   public static MimePart getMimePart(MimePart mp, String part) throws IOException, MessagingException {
/*  689 */     if (mp == null) {
/*  690 */       return null;
/*      */     }
/*  692 */     if ((part == null) || (part.trim().isEmpty())) {
/*  693 */       return mp;
/*      */     }
/*  695 */     part = part.trim();
/*      */     
/*  697 */     boolean digestParent = false;
/*  698 */     String[] subpart = part.split("\\.");
/*  699 */     for (int i = 0; i < subpart.length; i++) {
/*  700 */       int index = Integer.parseInt(subpart[i]);
/*  701 */       if (index <= 0) {
/*  702 */         return null;
/*      */       }
/*      */       
/*  705 */       String ct = getContentType(mp, digestParent ? "message/rfc822" : "text/plain");
/*  706 */       if (ct == null) {
/*  707 */         return null;
/*      */       }
/*  709 */       digestParent = ct.equals("multipart/digest");
/*      */       
/*  711 */       if (ct.startsWith("multipart/")) {
/*  712 */         MimeMultipart mmp = getMultipartContent(mp, ct);
/*  713 */         if ((mmp != null) && (mmp.getCount() >= index)) {
/*  714 */           BodyPart bp = mmp.getBodyPart(index - 1);
/*  715 */           if ((bp instanceof MimePart)) {
/*  716 */             mp = (MimePart)bp;
/*  717 */             continue;
/*      */           }
/*      */         }
/*  720 */       } else if ((ct.equals("message/rfc822")) || ((ct.equals("application/octet-stream")) && (isEmlAttachment(mp)))) {
/*  721 */         MimeMessage content = getMessageContent(mp);
/*  722 */         if (content != null) {
/*  723 */           if ((mp instanceof MimeMessage))
/*      */           {
/*  725 */             if (index != 1) {
/*  726 */               return null;
/*      */             }
/*      */           } else {
/*  729 */             i--;
/*      */           }
/*  731 */           mp = content;
/*  732 */           continue;
/*      */         }
/*  734 */       } else { if (((mp instanceof MimeMessage)) && (index == 1) && (i == subpart.length - 1)) {
/*      */           break;
/*      */         }
/*      */       }
/*  738 */       return null;
/*      */     }
/*  740 */     return mp;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private static boolean isFilterableAttachment(MPartInfo mpi, Set<MPartInfo> bodies)
/*      */   {
/*  755 */     if (mpi.isMultipart()) {
/*  756 */       return false;
/*      */     }
/*  758 */     MPartInfo parent = mpi.getParent();
/*  759 */     String ctype = mpi.getContentType();
/*      */     
/*  761 */     if (ctype.startsWith("text/"))
/*      */     {
/*  763 */       if ((parent == null) || ((mpi.getPartNum() == 1) && (parent.isMessage()))) {
/*  764 */         return false;
/*      */       }
/*      */       
/*  767 */       if ((bodies != null) && (bodies.contains(mpi))) {
/*  768 */         return false;
/*      */       }
/*      */       
/*  771 */       if (parent.getContentType().equals("multipart/alternative")) {
/*  772 */         return false;
/*      */       }
/*      */       
/*      */ 
/*  776 */       if ((mpi.getPartNum() == 1) && 
/*  777 */         (parent.isMultipart())) {
/*  778 */         MPartInfo pp = parent.getParent();
/*  779 */         if ((pp == null) || (pp.isMessage())) {
/*  780 */           return false;
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*      */ 
/*  786 */     if ((ctype.equals("xml/x-zimbra-share")) || (ctype.equals("xml/x-zimbra-dl-subscription")))
/*      */     {
/*  788 */       return false;
/*      */     }
/*      */     
/*      */ 
/*  792 */     if ((ctype.equals("message/disposition-notification")) || (ctype.equals("message/delivery-status"))) {
/*  793 */       return false;
/*      */     }
/*  795 */     return true;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static Set<String> getAttachmentTypeList(List<MPartInfo> parts)
/*      */   {
/*  805 */     Set<String> set = new HashSet();
/*  806 */     for (MPartInfo mpi : parts) {
/*  807 */       if (mpi.isFilterableAttachment()) {
/*  808 */         set.add(mpi.getContentType());
/*      */       }
/*      */     }
/*  811 */     return set;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   public static boolean hasAttachment(List<MPartInfo> parts)
/*      */   {
/*  819 */     for (MPartInfo mpi : parts) {
/*  820 */       if (mpi.mIsToplevelAttachment)
/*  821 */         return true;
/*      */     }
/*  823 */     return false;
/*      */   }
/*      */   
/*      */ 
/*      */   public static boolean hasTextCalenndar(List<MPartInfo> parts)
/*      */   {
/*  829 */     for (MPartInfo mpi : parts) {
/*  830 */       if ("text/calendar".equals(mpi.getContentType()))
/*  831 */         return true;
/*      */     }
/*  833 */     return false;
/*      */   }
/*      */   
/*  836 */   private static final javax.mail.internet.InternetAddress[] NO_ADDRESSES = new javax.mail.internet.InternetAddress[0];
/*      */   
/*      */   public static javax.mail.internet.InternetAddress[] parseAddressHeader(String header) {
/*  839 */     return parseAddressHeader(header, true);
/*      */   }
/*      */   
/*      */   public static javax.mail.internet.InternetAddress[] parseAddressHeader(MimeMessage mm, String headerName) {
/*  843 */     return parseAddressHeader(mm, headerName, true);
/*      */   }
/*      */   
/*      */   public static javax.mail.internet.InternetAddress[] parseAddressHeader(MimeMessage mm, String headerName, boolean expandGroups) {
/*      */     try {
/*  848 */       return parseAddressHeader(mm.getHeader(headerName, ","), expandGroups);
/*      */     } catch (MessagingException e) {}
/*  850 */     return NO_ADDRESSES;
/*      */   }
/*      */   
/*      */   public static javax.mail.internet.InternetAddress[] parseAddressHeader(String header, boolean expandGroups)
/*      */   {
/*  855 */     if ((header == null) || (header.trim().isEmpty()))
/*  856 */       return NO_ADDRESSES;
/*  857 */     header = header.trim();
/*      */     javax.mail.internet.InternetAddress[] addresses;
/*      */     try
/*      */     {
/*  861 */       addresses = JavaMailInternetAddress.parseHeader(header, false);
/*      */     }
/*      */     catch (Throwable e) {
/*  864 */       if ((e instanceof OutOfMemoryError)) {
/*  865 */         Zimbra.halt("MIME parser failed: " + header, e);
/*  866 */       } else if (!(e instanceof AddressException)) {
/*  867 */         sLog.error("MIME parser failed: " + header, e);
/*      */       }
/*      */       try {
/*  870 */         return new javax.mail.internet.InternetAddress[] { new JavaMailInternetAddress(null, header, "utf-8") };
/*      */       } catch (UnsupportedEncodingException e1) {
/*  872 */         return NO_ADDRESSES;
/*      */       }
/*      */     }
/*      */     
/*  876 */     if (!expandGroups)
/*  877 */       return addresses;
/*  878 */     boolean hasGroups = false;
/*  879 */     for (javax.mail.internet.InternetAddress addr : addresses) {
/*  880 */       if (addr.isGroup()) {
/*  881 */         hasGroups = true;
/*  882 */         break;
/*      */       }
/*      */     }
/*  885 */     if (!hasGroups) {
/*  886 */       return addresses;
/*      */     }
/*      */     
/*  889 */     List<javax.mail.internet.InternetAddress> expanded = new ArrayList();
/*  890 */     for (javax.mail.internet.InternetAddress addr : addresses) {
/*  891 */       if (!addr.isGroup()) {
/*  892 */         expanded.add(addr);
/*      */       } else {
/*      */         try {
/*  895 */           javax.mail.internet.InternetAddress[] members = addr.getGroup(false);
/*  896 */           if (members == null) {
/*  897 */             expanded.add(addr);
/*      */           } else
/*  899 */             for (javax.mail.internet.InternetAddress member : members)
/*  900 */               expanded.add(member);
/*      */         } catch (AddressException e) {
/*  902 */           expanded.add(addr);
/*      */         }
/*      */       }
/*      */     }
/*  906 */     return (javax.mail.internet.InternetAddress[])expanded.toArray(new javax.mail.internet.InternetAddress[expanded.size()]);
/*      */   }
/*      */   
/*  909 */   static Message.RecipientType[] sRcptTypes = { Message.RecipientType.TO, Message.RecipientType.CC, Message.RecipientType.BCC };
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static void removeRecipients(MimeMessage mm, String[] rcpts)
/*      */     throws MessagingException
/*      */   {
/*  919 */     for (Message.RecipientType rcptType : sRcptTypes) {
/*  920 */       Address[] addrs = mm.getRecipients(rcptType);
/*  921 */       if (addrs != null)
/*      */       {
/*  923 */         ArrayList<javax.mail.internet.InternetAddress> list = new ArrayList(addrs.length);
/*  924 */         for (int j = 0; j < addrs.length; j++) {
/*  925 */           javax.mail.internet.InternetAddress inetAddr = (javax.mail.internet.InternetAddress)addrs[j];
/*  926 */           String addr = inetAddr.getAddress();
/*  927 */           boolean match = false;
/*  928 */           for (int k = 0; k < rcpts.length; k++)
/*  929 */             if (addr.equalsIgnoreCase(rcpts[k]))
/*  930 */               match = true;
/*  931 */           if (!match)
/*  932 */             list.add(inetAddr);
/*      */         }
/*  934 */         if (list.size() < addrs.length) {
/*  935 */           javax.mail.internet.InternetAddress[] newRcpts = new javax.mail.internet.InternetAddress[list.size()];
/*  936 */           list.toArray(newRcpts);
/*  937 */           mm.setRecipients(rcptType, newRcpts);
/*      */         }
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */   public static final String getContentType(Multipart multi)
/*      */   {
/*  946 */     return getContentType(multi.getContentType());
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   public static final String getContentType(MimePart mp)
/*      */   {
/*  953 */     return getContentType(mp, "text/plain");
/*      */   }
/*      */   
/*      */ 
/*      */   public static final String getContentType(MimePart mp, String ctdefault)
/*      */   {
/*      */     try
/*      */     {
/*  961 */       String cthdr = mp.getHeader("Content-Type", null);
/*  962 */       if ((cthdr == null) || (cthdr.trim().isEmpty()))
/*  963 */         return ctdefault;
/*  964 */       return getContentType(cthdr);
/*      */     } catch (MessagingException e) {
/*  966 */       ZimbraLog.extensions.warn("could not fetch part's content-type; defaulting to text/plain", e); }
/*  967 */     return "text/plain";
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   public static final String getContentType(String cthdr)
/*      */   {
/*  975 */     return new ContentType(cthdr).getContentType().trim();
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static String decodeText(InputStream input, String contentType, String defaultCharset)
/*      */     throws IOException
/*      */   {
/*  989 */     StringBuilder buffer = new StringBuilder();
/*      */     try {
/*  991 */       Reader reader = getTextReader(input, contentType, defaultCharset);
/*  992 */       char[] cbuff = new char['ࠀ'];
/*      */       int num;
/*  994 */       while ((num = reader.read(cbuff, 0, cbuff.length)) != -1)
/*  995 */         buffer.append(cbuff, 0, num);
/*      */     } finally {
/*  997 */       ByteUtil.closeStream(input);
/*      */     }
/*  999 */     return buffer.toString();
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static Reader getTextReader(InputStream input, String contentType, String defaultCharset)
/*      */   {
/* 1018 */     Charset charset = CharsetUtil.toCharset(getCharset(contentType));
/* 1019 */     if (charset == null) {
/* 1020 */       if (!input.markSupported()) {
/* 1021 */         input = new BufferedInputStream(input);
/*      */       }
/* 1023 */       charset = detectCharset(input, CharsetUtil.toCharset(defaultCharset));
/*      */     }
/*      */     
/* 1026 */     return new InputStreamReader(input, CharsetUtil.normalizeCharset(charset));
/*      */   }
/*      */   
/*      */   private static Charset detectCharset(InputStream input, Charset defaultCharset) {
/* 1030 */     assert (input.markSupported());
/*      */     
/* 1032 */     if (defaultCharset == null) {
/* 1033 */       defaultCharset = Charset.defaultCharset();
/*      */     }
/* 1035 */     CharsetDetector detector = new CharsetDetector();
/*      */     try {
/* 1037 */       detector.setText(input);
/*      */     } catch (IOException e) {
/* 1039 */       return defaultCharset;
/*      */     }
/*      */     
/* 1042 */     for (CharsetMatch match : detector.detectAll()) {
/* 1043 */       if (match.getConfidence() <= 50)
/*      */         break;
/* 1045 */       try { return Charset.forName(match.getName());
/*      */       }
/*      */       catch (Exception ignore) {}
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/* 1053 */     return defaultCharset;
/*      */   }
/*      */   
/*      */   public static String getCharset(String contentType) {
/* 1057 */     if (contentType == null) {
/* 1058 */       return null;
/*      */     }
/* 1060 */     String charset = new ContentType(contentType).getParameter("charset");
/* 1061 */     if ((charset == null) || (charset.trim().isEmpty())) {
/* 1062 */       charset = null;
/*      */     }
/* 1064 */     return charset;
/*      */   }
/*      */   
/*      */   public static String encodeFilename(String filename)
/*      */   {
/*      */     try {
/* 1070 */       if (!StringUtil.isAsciiString(filename)) {
/* 1071 */         return new QCodec().encode(filename, "utf-8");
/*      */       }
/*      */     }
/*      */     catch (EncoderException ee) {}
/* 1075 */     return filename;
/*      */   }
/*      */   
/*      */   public static String getFilename(MimePart mp) {
/* 1079 */     String name = null;
/*      */     
/*      */     try
/*      */     {
/* 1083 */       String cdisp = mp.getHeader("Content-Disposition", null);
/* 1084 */       if (cdisp != null)
/*      */       {
/*      */ 
/* 1087 */         name = new ContentDisposition(cdisp).getParameter("filename");
/*      */       }
/*      */     }
/*      */     catch (MessagingException me) {}
/*      */     
/*      */ 
/* 1093 */     if (name == null) {
/*      */       try {
/* 1095 */         String ctype = mp.getHeader("Content-Type", null);
/* 1096 */         if (ctype != null)
/*      */         {
/*      */ 
/* 1099 */           name = new ContentType(ctype).getParameter("name");
/*      */         }
/*      */       }
/*      */       catch (MessagingException me) {}
/*      */     }
/*      */     
/* 1105 */     if (name == null) {
/* 1106 */       return null;
/*      */     }
/*      */     
/* 1109 */     name = StringUtil.sanitizeFilename(name);
/*      */     
/*      */ 
/* 1112 */     if ((name.indexOf("&#") != -1) && (name.indexOf(';') != -1)) {
/* 1113 */       return expandNumericCharacterReferences(name);
/*      */     }
/*      */     
/* 1116 */     return name;
/*      */   }
/*      */   
/*      */   public static String expandNumericCharacterReferences(String raw) {
/* 1120 */     if (raw == null) {
/* 1121 */       return null;
/*      */     }
/*      */     
/* 1124 */     int start = -1;
/* 1125 */     boolean hex = false;
/* 1126 */     int calc = 0;
/*      */     
/* 1128 */     StringBuilder sb = new StringBuilder();
/* 1129 */     int i = 0; for (int len = raw.length(); i < len; i++) {
/* 1130 */       char c = raw.charAt(i);
/* 1131 */       if (start != -1) {
/* 1132 */         if ((c >= '0') && (c <= '9')) {
/* 1133 */           calc = calc * (hex ? 16 : 10) + c - 48;
/* 1134 */         } else if ((hex) && (c >= 'a') && (c <= 'f')) {
/* 1135 */           calc = calc * 16 + 10 + c - 97;
/* 1136 */         } else if ((hex) && (c >= 'A') && (c <= 'F')) {
/* 1137 */           calc = calc * 16 + 10 + c - 65;
/* 1138 */         } else { if (c == ';') if (i > start + (hex ? 4 : 3)) {
/* 1139 */               sb.append((char)calc);
/* 1140 */               start = -1; continue;
/*      */             }
/* 1142 */           sb.append(raw.substring(start, i--));
/* 1143 */           start = -1;
/*      */         }
/* 1145 */       } else if ((c == '&') && (i < len - 3) && (raw.charAt(i + 1) == '#')) {
/* 1146 */         hex = (raw.charAt(i + 2) == 'x') || (raw.charAt(i + 2) == 'X');
/* 1147 */         start = i;
/* 1148 */         i += (hex ? 2 : 1);
/* 1149 */         calc = 0;
/*      */       } else {
/* 1151 */         sb.append(c);
/*      */       }
/*      */     }
/* 1154 */     if (start != -1) {
/* 1155 */       sb.append(raw.substring(start));
/*      */     }
/*      */     
/* 1158 */     return sb.toString();
/*      */   }
/*      */   
/*      */   public static MPartInfo getTextBody(List<MPartInfo> parts, boolean preferHtml) {
/* 1162 */     for (MPartInfo mpi : getBody(parts, preferHtml)) {
/* 1163 */       if (mpi.getContentType().startsWith("text/")) {
/* 1164 */         return mpi;
/*      */       }
/*      */     }
/* 1167 */     return null;
/*      */   }
/*      */   
/*      */   public static Set<MPartInfo> getBody(List<MPartInfo> parts, boolean preferHtml) {
/* 1171 */     if (parts.isEmpty()) {
/* 1172 */       return Collections.emptySet();
/*      */     }
/*      */     
/* 1175 */     Set<MPartInfo> bodies = null;
/*      */     
/*      */ 
/* 1178 */     MPartInfo top = (MPartInfo)parts.get(0);
/* 1179 */     if (!top.isMultipart()) {
/* 1180 */       if (!top.getDisposition().equals("attachment")) {
/* 1181 */         (bodies = new HashSet(1)).add(top);
/*      */       }
/*      */     } else {
/* 1184 */       bodies = getBodySubparts(top, preferHtml);
/*      */     }
/*      */     
/* 1187 */     if (bodies == null) {
/* 1188 */       bodies = Collections.emptySet();
/*      */     }
/* 1190 */     return bodies;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   public static String getHeader(MimePart part, String headerName)
/*      */   {
/*      */     try
/*      */     {
/* 1200 */       String value = part.getHeader(headerName, null);
/* 1201 */       if ((value == null) || (value.isEmpty())) {
/* 1202 */         return null;
/*      */       }
/*      */       try
/*      */       {
/* 1206 */         value = MimeUtility.decodeText(value);
/*      */       }
/*      */       catch (UnsupportedEncodingException e) {}
/* 1209 */       return MimeUtility.unfold(value);
/*      */     }
/*      */     catch (MessagingException e) {
/* 1212 */       sLog.debug("Unable to get header '%s'", headerName, e); }
/* 1213 */     return null;
/*      */   }
/*      */   
/*      */ 
/* 1217 */   private static final String[] NO_HEADERS = new String[0];
/*      */   
/*      */ 
/*      */ 
/*      */   public static String[] getHeaders(MimePart part, String headerName)
/*      */   {
/*      */     try
/*      */     {
/* 1225 */       String[] values = part.getHeader(headerName);
/* 1226 */       if ((values == null) || (values.length == 0)) {
/* 1227 */         return NO_HEADERS;
/*      */       }
/* 1229 */       for (int i = 0; i < values.length; i++) {
/*      */         try {
/* 1231 */           values[i] = MimeUtility.decodeText(values[i]);
/*      */         }
/*      */         catch (UnsupportedEncodingException e) {}
/*      */         
/* 1235 */         values[i] = MimeUtility.unfold(values[i]);
/*      */       }
/*      */       
/* 1238 */       return values;
/*      */     } catch (MessagingException e) {
/* 1240 */       sLog.debug("Unable to get headers named '%s'", headerName, e); }
/* 1241 */     return NO_HEADERS;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   public static String getMessageID(MimeMessage mm)
/*      */   {
/*      */     try
/*      */     {
/* 1251 */       return Strings.emptyToNull(mm.getMessageID());
/*      */     } catch (MessagingException e) {}
/* 1253 */     return null;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   public static String getSubject(MimeMessage mm)
/*      */     throws MessagingException
/*      */   {
/* 1262 */     String subject = mm.getHeader("Subject", null);
/* 1263 */     return subject == null ? null : ZInternetHeader.decode(subject);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   public static String getSender(MimeMessage mm)
/*      */   {
/* 1270 */     String sender = null;
/*      */     try {
/* 1272 */       sender = mm.getHeader("From", null);
/*      */     }
/*      */     catch (MessagingException e) {}
/* 1275 */     if (sender == null) {
/*      */       try {
/* 1277 */         sender = mm.getHeader("Sender", null);
/*      */       }
/*      */       catch (MessagingException e) {}
/*      */     }
/* 1281 */     if (sender == null) {
/* 1282 */       sender = "";
/* 1283 */     } else if (sender.endsWith("<>")) {
/* 1284 */       sender = sender.replaceAll("<>$", "").trim();
/*      */     }
/* 1286 */     return sender;
/*      */   }
/*      */   
/*      */   private static Set<MPartInfo> getBodySubparts(MPartInfo base, boolean preferHtml)
/*      */   {
/* 1291 */     if ((!base.hasChildren()) || (base.isMessage())) {
/* 1292 */       return null;
/*      */     }
/* 1294 */     String ctype = base.getContentType();
/*      */     
/* 1296 */     if (ctype.equals("multipart/alternative"))
/* 1297 */       return getAlternativeBodySubpart(base.getChildren(), preferHtml);
/* 1298 */     if (ctype.equals("multipart/related"))
/* 1299 */       return getRelatedBodySubpart(base.getChildren(), preferHtml, base.getContentTypeParameter("start"));
/* 1300 */     if (ctype.equals("multipart/report"))
/* 1301 */       return getReportBodySubpart(base.getChildren(), preferHtml);
/* 1302 */     List<MPartInfo> children; List<MPartInfo> children; if ((ctype.equals("multipart/mixed")) || (!KNOWN_MULTIPART_TYPES.contains(ctype))) {
/* 1303 */       children = base.getChildren();
/*      */     } else {
/* 1305 */       children = Arrays.asList(new MPartInfo[] { (MPartInfo)base.getChildren().get(0) });
/*      */     }
/*      */     
/* 1308 */     Set<MPartInfo> bodies = null;
/* 1309 */     for (MPartInfo mpi : children) {
/* 1310 */       if (mpi.isMultipart()) {
/* 1311 */         Set<MPartInfo> found = getBodySubparts(mpi, preferHtml);
/* 1312 */         if (found != null) {
/* 1313 */           if (bodies == null)
/* 1314 */             bodies = new LinkedHashSet(found.size());
/* 1315 */           bodies.addAll(found);
/*      */         }
/* 1317 */       } else if ((!mpi.getDisposition().equals("attachment")) && (!mpi.isMessage()) && ((mpi.getContentID() == null) || (mpi.getContentType().matches("text/.*"))))
/*      */       {
/* 1319 */         if (bodies == null)
/* 1320 */           bodies = new LinkedHashSet(1);
/* 1321 */         bodies.add(mpi);
/*      */       }
/*      */     }
/*      */     
/* 1325 */     return bodies;
/*      */   }
/*      */   
/*      */   private static <T> Set<T> setContaining(T mpi) {
/* 1329 */     Set<T> body = new LinkedHashSet(1);
/* 1330 */     body.add(mpi);
/* 1331 */     return body;
/*      */   }
/*      */   
/*      */   private static Set<MPartInfo> getAlternativeBodySubpart(List<MPartInfo> children, boolean preferHtml)
/*      */   {
/* 1336 */     MPartInfo alternative = null;
/* 1337 */     for (MPartInfo mpi : children) {
/* 1338 */       boolean isAttachment = mpi.getDisposition().equals("attachment");
/*      */       
/* 1340 */       String wantType = preferHtml ? "text/html" : "text/plain";
/* 1341 */       Set<String> altTypes = preferHtml ? HTML_ALTERNATES : TEXT_ALTERNATES;
/*      */       
/* 1343 */       String ctype = mpi.getContentType();
/* 1344 */       if ((!isAttachment) && (ctype.equals(wantType)))
/* 1345 */         return setContaining(mpi);
/* 1346 */       if ((!isAttachment) && (altTypes.contains(ctype))) {
/* 1347 */         if ((alternative == null) || (!alternative.getContentType().equalsIgnoreCase(ctype))) {
/* 1348 */           alternative = mpi;
/*      */         }
/* 1350 */       } else if (mpi.isMultipart()) {
/*      */         Set<MPartInfo> body;
/* 1352 */         if ((body = getBodySubparts(mpi, preferHtml)) != null) {
/* 1353 */           return body;
/*      */         }
/*      */       }
/*      */     }
/* 1357 */     if (alternative == null)
/* 1358 */       return null;
/* 1359 */     return setContaining(alternative);
/*      */   }
/*      */   
/*      */   private static Set<MPartInfo> getRelatedBodySubpart(List<MPartInfo> children, boolean preferHtml, String parentCID)
/*      */   {
/* 1364 */     if (parentCID != null) {
/* 1365 */       for (MPartInfo mpi : children) {
/* 1366 */         if (parentCID.equals(mpi.getContentID()))
/*      */         {
/*      */ 
/* 1369 */           if (mpi.isMultipart()) {
/* 1370 */             return getBodySubparts(mpi, preferHtml);
/*      */           }
/* 1372 */           return setContaining(mpi);
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*      */ 
/* 1378 */     MPartInfo first = null;
/* 1379 */     for (MPartInfo mpi : children) {
/* 1380 */       if (mpi.getContentType().startsWith("text/"))
/* 1381 */         return setContaining(mpi);
/* 1382 */       if (mpi.isMultipart())
/* 1383 */         return getBodySubparts(mpi, preferHtml);
/* 1384 */       if (first == null) {
/* 1385 */         first = mpi;
/*      */       }
/*      */     }
/*      */     
/*      */ 
/* 1390 */     if (first == null)
/* 1391 */       return null;
/* 1392 */     return setContaining(first);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   private static Set<MPartInfo> getReportBodySubpart(List<MPartInfo> children, boolean preferHtml)
/*      */   {
/* 1399 */     Set<MPartInfo> subparts = new HashSet();
/* 1400 */     Set<MPartInfo> alternatives = new HashSet();
/* 1401 */     Set<MPartInfo> headers = new HashSet();
/* 1402 */     for (MPartInfo mpi : children) {
/* 1403 */       boolean isAttachment = mpi.getDisposition().equals("attachment");
/*      */       
/* 1405 */       String wantType = preferHtml ? "text/html" : "text/plain";
/* 1406 */       Set<String> altTypes = preferHtml ? HTML_ALTERNATES : TEXT_ALTERNATES;
/*      */       
/* 1408 */       String ctype = mpi.getContentType();
/* 1409 */       if ((!isAttachment) && (ctype.equals(wantType))) {
/* 1410 */         subparts.add(mpi);
/* 1411 */       } else if ((!isAttachment) && (altTypes.contains(ctype))) {
/* 1412 */         alternatives.add(mpi);
/* 1413 */       } else if ((!isAttachment) && (ctype.equals("text/rfc822-headers"))) {
/* 1414 */         headers.add(mpi);
/* 1415 */       } else if (mpi.isMultipart()) {
/*      */         Set<MPartInfo> body;
/* 1417 */         if ((body = getBodySubparts(mpi, preferHtml)) != null)
/* 1418 */           subparts.addAll(body);
/*      */       }
/*      */     }
/* 1421 */     if (subparts.size() == 0) {
/* 1422 */       alternatives.addAll(headers);
/* 1423 */       return alternatives.size() == 0 ? null : alternatives;
/*      */     }
/* 1425 */     subparts.addAll(headers);
/* 1426 */     return subparts;
/*      */   }
/*      */   
/*      */   public static void main(String[] args)
/*      */     throws MessagingException, IOException
/*      */   {
/* 1432 */     String s = URLDecoder.decode("Zimbra%20&#26085;&#26412;&#35486;&#21270;&#12398;&#32771;&#24942;&#28857;.txt", "utf-8");
/* 1433 */     System.out.println(s);
/* 1434 */     System.out.println(expandNumericCharacterReferences("Zimbra%20&#26085;&#26412;&#35486;&#21270;&#12398;&#32771;&#24942;&#28857;.txt&#x40;&;&#;&#x;&#&#3876;&#55"));
/*      */     
/* 1436 */     MimeMessage mm = new FixedMimeMessage(JMSession.getSession(), new SharedFileInputStream("C:\\Temp\\mail\\24245"));
/* 1437 */     InputStream is = new RawContentMultipartDataSource(mm, new ContentType(mm.getContentType())).getInputStream();
/*      */     
/* 1439 */     byte[] buf = new byte['Ѐ'];
/* 1440 */     int num; while ((num = is.read(buf)) != -1) {
/* 1441 */       System.out.write(buf, 0, num);
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static InputStream getInputStream(MimeMessage mm)
/*      */     throws IOException
/*      */   {
/* 1457 */     PipedInputStream in = new PipedInputStream();
/* 1458 */     PipedOutputStream out = new PipedOutputStream(in);
/* 1459 */     Thread thread = new Thread(new MimeMessageOutputThread(mm, out));
/* 1460 */     thread.setName("MimeMessageThread");
/* 1461 */     thread.start();
/* 1462 */     return in;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   public static int getSize(MimePart part)
/*      */     throws MessagingException, IOException
/*      */   {
/* 1470 */     int size = part.getSize();
/* 1471 */     if (size > 0) {
/* 1472 */       if ("base64".equalsIgnoreCase(part.getEncoding()))
/*      */       {
/* 1474 */         int lines = (size + 77) / 78;
/* 1475 */         size = (int)(0.75D * (size - 2 * lines));
/*      */       }
/*      */     } else {
/* 1478 */       size = (int)ByteUtil.getDataLength(part.getInputStream());
/*      */     }
/* 1480 */     return size;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   public static boolean isAutoSubmitted(MimePart part)
/*      */     throws MessagingException
/*      */   {
/* 1488 */     String[] autoSubmitted = part.getHeader("Auto-Submitted");
/* 1489 */     if (autoSubmitted != null) {
/* 1490 */       for (int i = 0; i < autoSubmitted.length; i++) {
/* 1491 */         if (!autoSubmitted[i].equalsIgnoreCase("no")) {
/* 1492 */           return true;
/*      */         }
/*      */       }
/*      */     }
/* 1496 */     return false;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   public static List<String> getReferences(MimeMessage mm, String header)
/*      */   {
/*      */     try
/*      */     {
/* 1505 */       return getReferences(mm.getHeader(header, " "));
/*      */     } catch (MessagingException e) {}
/* 1507 */     return new ArrayList(0);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   public static List<String> getReferences(InternetHeaders headers, String header)
/*      */   {
/* 1515 */     return getReferences(headers.getHeader(header, " "));
/*      */   }
/*      */   
/*      */   private static List<String> getReferences(String hvalue)
/*      */   {
/* 1520 */     String value = StringUtil.stripControlCharacters(hvalue);
/*      */     
/* 1522 */     if (Strings.isNullOrEmpty(value)) {
/* 1523 */       return new ArrayList(0);
/*      */     }
/*      */     
/* 1526 */     List<String> refs = new ArrayList();
/* 1527 */     boolean quoted = false;boolean escaped = false;boolean empty = true;
/* 1528 */     int pos = 0;int astart = pos;int end = value.length();int clevel = 0;
/*      */     
/* 1530 */     while (pos < end) {
/* 1531 */       char c = value.charAt(pos++);
/* 1532 */       if ((c == '\r') || (c == '\n')) {
/* 1533 */         escaped = false;
/* 1534 */       } else if (quoted) {
/* 1535 */         quoted = (escaped) || (c != '"');
/* 1536 */         escaped = (!escaped) && (c == '\\');
/* 1537 */       } else if ((c == '(') || (clevel > 0)) {
/* 1538 */         if ((!escaped) && ((c == '(') || (c == ')'))) {
/* 1539 */           clevel += (c == '(' ? 1 : -1);
/*      */         }
/* 1541 */         escaped = (!escaped) && (c == '\\');
/* 1542 */       } else if (c == '"') {
/* 1543 */         quoted = true;
/* 1544 */         empty = false;
/* 1545 */       } else if (c == '>') {
/* 1546 */         if (!empty) {
/* 1547 */           refs.add(new com.zimbra.common.mime.InternetAddress(value.substring(astart, pos)).getAddress());
/*      */         }
/* 1549 */         empty = true;
/* 1550 */         astart = pos;
/* 1551 */       } else if ((c != ' ') && (c != '\t') && (empty)) {
/* 1552 */         empty = false;
/*      */       }
/*      */     }
/* 1555 */     if (!empty) {
/* 1556 */       refs.add(new com.zimbra.common.mime.InternetAddress(value.substring(astart, pos)).getAddress());
/*      */     }
/*      */     
/* 1559 */     return refs;
/*      */   }
/*      */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/mime/Mime.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */